1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/*!
Flags for `isotope` terms
*/
use super::*;
use std::sync::atomic::{AtomicU8, Ordering::*};

/// A flag which an `isotope` term may have set
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, IntoEnumIterator)]
pub enum TyckFlag {
    /// Whether this term's *sub-terms* type-check
    GlobalTyck = 0,
    /// Whether this term *locally* type-checks
    LocalTyck = 2,
    /// Whether this term's *variables* type-check
    VarTyck = 4,
    /// Whether this term's *annotation* type-checks
    AnnotTyck = 6,
}

/// A set of flags for an `isotope` term
#[derive(Copy, Clone, Eq, PartialEq, Hash, Default)]
pub struct TyckFlags(u8);

impl TyckFlags {
    /// The empty set of flags, i.e. all flags unknown
    pub const EMPTY: TyckFlags = TyckFlags(0b0);
    /// The set of flags for a nil term
    pub const NIL: TyckFlags = Self::ALL_FALSE;
    /// All flags true
    pub const ALL_TRUE: TyckFlags = TyckFlags(EVEN_MASK_U8);
    /// All flags false
    pub const ALL_FALSE: TyckFlags = TyckFlags(ODD_MASK_U8);

    /// Get the value of a given flag
    #[inline]
    pub const fn get_flag(self, flag: TyckFlag) -> L4 {
        L4::from_u8(self.0.wrapping_shr(flag as u32) & 0b11)
    }
    /// Store the value of a flag
    #[inline]
    pub fn store_flag(&mut self, flag: TyckFlag, value: L4) {
        let mask = 0b11 << flag as u32;
        self.0 &= !mask;
        self.0 |= (value as u8) << flag as u32;
    }
    /// Get this flag-set with one flag having a given value
    #[inline]
    pub const fn with_flag(self, flag: TyckFlag, value: L4) -> TyckFlags {
        let mask = 0b11 << flag as u32;
        let mut flags = self.0;
        flags &= !mask;
        flags |= (value as u8) << flag as u32;
        TyckFlags(flags)
    }
    /// Set the value of a flag
    #[inline]
    pub fn set_flag(&mut self, flag: TyckFlag, value: L4) {
        self.0 |= (value as u8) << flag as u32;
    }
    /// Get this flag-set after one flag has been set to a given value
    #[inline]
    pub const fn with_flag_set(self, flag: TyckFlag, value: L4) -> TyckFlags {
        TyckFlags(self.0 | ((value as u8) << flag as u32))
    }
    /// Take the conjunction of this flag-set with another
    #[inline]
    pub const fn conjunction(self, other: TyckFlags) -> TyckFlags {
        TyckFlags(self.0 & ODD_MASK_U8 | other.0 & ODD_MASK_U8 | (self.0 & other.0))
    }
    /// Take the disjunction of this flag-set with another
    #[inline]
    pub const fn disjunction(self, other: TyckFlags) -> TyckFlags {
        TyckFlags(self.0 & EVEN_MASK_U8 | other.0 & EVEN_MASK_U8 | (self.0 & other.0))
    }
    /// Get the negation of this flag-set
    #[inline]
    pub const fn negation(self) -> TyckFlags {
        TyckFlags((self.0 & EVEN_MASK_U8 << 1) | ((self.0 & ODD_MASK_U8) >> 1))
    }
    /// Whether this flag set type-checks
    #[inline]
    pub fn get_tyck(self) -> L4 {
        let result = self
            .get_flag(GlobalTyck)
            .conjunction(self.get_flag(AnnotTyck))
            .conjunction(self.get_flag(VarTyck))
            .conjunction(self.get_flag(GlobalTyck))
            .conjunction(self.get_flag(LocalTyck));
        result
    }
    /// Whether this flag set might type-check
    #[inline]
    pub const fn maybe_tyck(self) -> bool {
        self.get_flag(GlobalTyck).maybe_true()
            && self.get_flag(AnnotTyck).maybe_true()
            && self.get_flag(LocalTyck).maybe_true()
    }
}

impl Index<TyckFlag> for TyckFlags {
    type Output = L4;

    fn index(&self, index: TyckFlag) -> &Self::Output {
        self.get_flag(index).as_static()
    }
}

impl Debug for TyckFlags {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "TyckFlags(4'b{:?}{:?}{:?}{:?})",
            self.get_flag(TyckFlag::GlobalTyck),
            self.get_flag(TyckFlag::LocalTyck),
            self.get_flag(TyckFlag::VarTyck),
            self.get_flag(TyckFlag::AnnotTyck),
        )
    }
}

/// An atomic set of flags for an `isotope` term
#[derive(Default)]
pub struct AtomicTyckFlags(AtomicU8);

impl From<TyckFlags> for AtomicTyckFlags {
    #[inline]
    fn from(f: TyckFlags) -> Self {
        AtomicTyckFlags(AtomicU8::new(f.0))
    }
}

impl AtomicTyckFlags {
    /// Load the current set of flags
    #[inline]
    pub fn load_flags(&self) -> TyckFlags {
        TyckFlags(self.0.load(Relaxed))
    }
    /// Load the current value of a given flag
    #[inline]
    pub fn load_flag(&self, flag: TyckFlag) -> L4 {
        self.load_flags().get_flag(flag)
    }
    /// Store the given flags
    #[inline]
    pub fn store_flags(&self, flags: TyckFlags) {
        self.0.store(flags.0, Relaxed)
    }
    /// Store a flag with a given value, returning the *previous* flags
    #[inline]
    pub fn store_flag(&self, flag: TyckFlag, value: L4) {
        self.0
            .fetch_update(Relaxed, Relaxed, move |flags| {
                Some(TyckFlags(flags).with_flag(flag, value).0)
            })
            .expect("Provided function never returns None");
    }
    /// Set the given flags
    #[inline]
    pub fn set_flags(&self, flags: TyckFlags) -> TyckFlags {
        TyckFlags(self.0.fetch_or(flags.0, Relaxed))
    }
    /// Set a flag with a given value
    #[inline]
    pub fn set_flag(&self, flag: TyckFlag, value: L4) -> TyckFlags {
        TyckFlags(self.0.fetch_or((value as u8) << flag as u32, Relaxed))
    }
}

impl Index<TyckFlag> for AtomicTyckFlags {
    type Output = L4;

    fn index(&self, index: TyckFlag) -> &Self::Output {
        self.load_flag(index).as_static()
    }
}

impl Debug for AtomicTyckFlags {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "Atomic{:?}", TyckFlags(self.0.load(Relaxed)))
    }
}

impl Clone for AtomicTyckFlags {
    #[inline]
    fn clone(&self) -> Self {
        AtomicTyckFlags(AtomicU8::new(self.0.load(Relaxed)))
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn basic_single_flag_properties() {
        for flag in TyckFlag::into_enum_iter() {
            assert_eq!(flag as u32 % 2, 0);
            let mut flags = TyckFlags::default();
            let atomic_flags = AtomicTyckFlags::default();
            assert_eq!(flags, TyckFlags::EMPTY);
            assert_eq!(atomic_flags.load_flags(), flags);
            flags.set_flag(flag, true.into());
            atomic_flags.set_flag(flag, true.into());
            assert_eq!(atomic_flags.load_flags(), flags);
            assert_ne!(flags, TyckFlags::EMPTY);
            assert_eq!(flags, TyckFlags::EMPTY.with_flag_set(flag, true.into()));
            flags.set_flag(flag, false.into());
            atomic_flags.set_flag(flag, false.into());
            assert_eq!(atomic_flags.load_flags(), flags);
            assert_ne!(flags, TyckFlags::EMPTY.with_flag_set(flag, true.into()));
            assert_ne!(flags, TyckFlags::EMPTY.with_flag_set(flag, false.into()));
            assert_eq!(flags, TyckFlags::EMPTY.with_flag_set(flag, L4::Both));
            assert_eq!(
                flags,
                TyckFlags::EMPTY
                    .with_flag_set(flag, true.into())
                    .with_flag_set(flag, false.into())
            );
            flags.store_flag(flag, false.into());
            atomic_flags.store_flag(flag, false.into());
            assert_eq!(atomic_flags.load_flags(), flags);
            assert_ne!(flags, TyckFlags::EMPTY.with_flag_set(flag, true.into()));
            assert_eq!(flags, TyckFlags::EMPTY.with_flag_set(flag, false.into()));
            assert_ne!(flags, TyckFlags::EMPTY.with_flag_set(flag, L4::Both));
            assert_eq!(
                flags,
                TyckFlags::EMPTY
                    .with_flag(flag, true.into())
                    .with_flag(flag, false.into())
            );
            atomic_flags.set_flags(TyckFlags::EMPTY);
            assert_eq!(atomic_flags.load_flags(), flags);
            atomic_flags.store_flags(TyckFlags::EMPTY);
            assert_ne!(atomic_flags.load_flags(), flags);
            assert_eq!(atomic_flags.load_flags(), TyckFlags::EMPTY);
        }
    }
}